from helper import *
from time import sleep
from iptable import get_ip_by_wtype,MIB_IPADDR_DYNAMIC
import sys
import threading
import socket
import struct
import signal




sync_text = ""
peers = get_peers()

default_ips = get_ip_by_wtype(MIB_IPADDR_DYNAMIC)
default_port = 2333



def signal_handler(signalnum,handler):
    if signalnum == signal.SIGINT:
        log("Info","Signal SIGINT raise. Main thread quit.")
    elif signalnum == signal.SIGTERM:
        log("Info","Signal SIGTERM raise. Main thread quit.")
    else:
        log("ERROR","Signal Unknown raise. Main thread quit.")
    sys.exit()


signal.signal(signal.SIGINT,signal_handler)
signal.signal(signal.SIGTERM,signal_handler)


class AhoPack():

    aho_struct =  struct.Struct("!BH")

    def __init__(self,data,op_code=0):
        self.op_code = op_code
        self.data =  data.encode()
        self.lenght = len(self.data) + 3


    def pack(self):

        return  self.aho_struct.pack(self.op_code,self.lenght) + self.data  


def sync_with_peers(peers,data):
    pack = AhoPack(data,1)
    bin_data =  pack.pack()
    for peer in peers:
        try:
            sock = socket.socket()
            sock.settimeout(1)
            sock.connect(peer)
            sock.send(bin_data)
            sock.close()
        except ConnectionRefusedError:
            log("ERROR",f"{peer[0]}:{peer[1]} Refused.")
            sock.close()
        except socket.timeout:
            log("ERROR",f"{peer[0]}:{peer[1]} Timeout.")
            sock.close()
        



def short_text(text):
    if len(text) >= 20:
        show_text = text[0:19]
        show_text += "..."
    else:
        show_text = text
    return show_text


class ServerThread(threading.Thread):
    def __init__(self,data_handle,addr=("",7773)):
        threading.Thread.__init__(self)
        self.addr = addr
        self.data_handle = data_handle
    
    def connect_handle(self,peer,addr):
        op_code = int.from_bytes(peer.recv(1),'little')
        length = int.from_bytes(peer.recv(2),'big')
        if length < 4:
            peer.close()
            log("WARING","Receive *empty* DATA.")
            return
        data = peer.recv(length-3)
        data = data.decode("utf-8")
        log("Receive","{}:{} op_code {},length {} : \"{}\"".format(
                        addr[0],
                        addr[1],
                        op_code,
                        length,
                        short_text(data)
                    ))
        self.data_handle(data)
        peer.close()


    def run(self):
        
        s = socket.socket()
        try:
            s.bind(self.addr)
            log("Info",f"Server running on {self.addr[0]}:{self.addr[1]}.")
        except:
            log("ERROR",f"When bind address {self.addr[0]}:{self.addr[1]}.")
            log("ERROR","Server shutdown.")
            s.close()
            return
        
        s.listen()
        try:
            while True:
                peer,addr = s.accept()
                self.connect_handle(peer,addr)
        finally:
            s.close()
            log("Info","Server shutdown.")
        

    

def aho_decoder(raw_data):
    global sync_text
    sync_text = raw_data
    set_clipboard_text(raw_data)


args = len(sys.argv)
if args == 3:
    address = (sys.argv[1],int(sys.argv[2]))
elif args == 2:
    try:
        port = int(sys.argv[1])
    except ValueError:
        log("ERROR",f"Invail port \"{sys.argv[1]}\".")
        sys.exit()
    except:
        log("ERROR","Unexcept errors occur.")
        sys.exit()
    address = (default_ips[0],port)
elif args == 1:
    address = (default_ips[0],default_port)
else:
    log("ERROR","Mistaken arguments.")
    sys.exit()

server = ServerThread(aho_decoder,address)
server.setDaemon(True)
server.start()
try:
    sync_text = get_clipboard_text()
except ValueError as ve:
    log("Info",ve)
    sync_text = ""

temp = ""
while True:
    sleep(0.6)
    try:
        temp = get_clipboard_text()
    except ValueError as ve:
        continue
    
    if sync_text != temp:
        log("Info",f"Cilpboard modified \"{short_text(temp)}\".")
        sync_text = temp
        peers_thread = threading.Thread(target=sync_with_peers,args=[peers,temp])
        peers_thread.run()
    if not server.is_alive():
        log("ERROR","Main thread quit due to server shutdown.")
        sys.exit() 
